Лекция 1 Базовый SQL


In [2]:
%load_ext sql
%sql sqlite://


The sql extension is already loaded. To reload it, use:
  %reload_ext sql
Out[2]:
'Connected: None@None'

Давайте создадим таблицу, заполним ее и сделаем какой-нибудь запрос!


In [4]:
%%sql drop table if exists product;
create table product(
       pname        varchar primary key, -- имя продукта
       price        money,               -- цена продукта
       category     varchar,             -- категория
       manufacturer varchar NOT NULL     -- производитель
);
insert into product values('Пряник', 19.99, 'Еда', 'Тульский пряник');
insert into product values('Тетрадь', 29.99, 'Канцелярия', 'Эксмо');
insert into product values('Печенье', 149.99, 'Еда', 'WagonWiels');
insert into product values('Планшет', 203.99, 'Техника', 'Zua');


Done.
0 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
Out[4]:
[]

Посмотрим на полученную таблицу.


In [5]:
%sql select * from product;


Done.
Out[5]:
pname price category manufacturer
Пряник 19.99 Еда Тульский пряник
Тетрадь 29.99 Канцелярия Эксмо
Печенье 149.99 Еда WagonWiels
Планшет 203.99 Техника Zua

Немного SQL терминологии

  • имя таблицы - product.
  • Каждая строка таблицы называется строкой или кортеж.
  • Заметьте, что все кортежи имеют поля или атрибуты.
  • Количество строк называет мощностью, в то время как количество атрибутов арностью

Соглашения схемы

  • Схема таблицы продукта может быть описана следующим образом:

product(pname, price, category, manufacturer)

Подчеркнутое множество атрибутов формирует ключ.

  • В этом случае ключ - pname. Если имя продукта было бы только уникальным для указнного производителя мы бы написали:

product(pname, price, category, manufacturer)

Описание таблиц

  • Кортеж = запись
    • Ограничение: все атрибуты - атомарного типа
    • Существует множество атомарных типов для SQL, например, по postgresQL можно посмотреть здесь .
  • Таблица = мультимножество кортежей
    • Мультимножество похоже на список
    • ... Но по определению оно не упорядочено:
      • нет first(), нет next(), нет last().

Итог

  • Создана база данных -- сделано!

  • Простые запросы -- сейчас!

  • Запрос с более чем одной таблицей -- далее!

Простые запросы

  • Вспомним основы SQL на примере
  • В интернет очень много SQL туториалов, сейчас мы всего лишь вспомним базовые вещи

SELECT <Множество атрибутов>
FROM <список таблиц и условие на соединение>
WHERE <список условий>

Это простейший SELECT-FROM-WHERE (SFW) блок. Давайте посмотрим на примерах!


In [6]:
%%sql
SELECT * from Product 
WHERE  category = 'Канцелярия' and manufacturer = 'Эксмо'


Done.
Out[6]:
pname price category manufacturer
Тетрадь 29.99 Канцелярия Эксмо

Посмотрим на примеры проекции, то есть получим только несколько атрибутов запроса.


In [9]:
%%sql 
SELECT Pname, Price, Manufacturer
FROM Product;


Done.
Out[9]:
pname price manufacturer
Пряник 19.99 Тульский пряник
Тетрадь 29.99 Эксмо
Печенье 149.99 WagonWiels
Планшет 203.99 Zua
  • На выходе все еще таблица и ее схема -

    Answer(pname, price, manufacturer)

  • Можно объединять выборку и проекцию

In [14]:
%%sql
SELECT Pname, Price, Manufacturer
FROM Product
WHERE category='Еда';


Done.
Out[14]:
pname price manufacturer
Пряник 19.99 Тульский пряник
Печенье 149.99 WagonWiels

На выходе запроса к таблице - снова таблица


In [16]:
%%sql
SELECT
    p.manufacturer, p.pname, p.price
FROM 
    (SELECT distinct p0.category
     FROM Product p0
     WHERE p0.price < 20.00) cp, -- this is a nested query!
    Product p
WHERE 
    p.category = cp.category and p.price > 20.00


Done.
Out[16]:
manufacturer pname price
WagonWiels Печенье 149.99

Небольшие детали

  • Некоторые элементы регистро-независимые:
    • Одно и то же: SELECT Select select
    • Одно и то же: Product product
    • Разные: ‘Seattle’ ‘seattle’
  • Константы (одинарные кавычки)
    • ‘abc’ - да
    • “abc” - нет

In [17]:
%sql select * from product;


Done.
Out[17]:
pname price category manufacturer
Пряник 19.99 Еда Тульский пряник
Тетрадь 29.99 Канцелярия Эксмо
Печенье 149.99 Еда WagonWiels
Планшет 203.99 Техника Zua

LIKE

Опертоор LIKE нужен для поиска строк:

SELECT * FROM Products WHERE pname like '%gizmo%'

Like - "сильно урезанный" regexp

  • % - сколько угодно символов
  • _ ровно один символ
  • оператор LIKE - регистрозависимый

In [19]:
%%sql
SELECT *  FROM product
where category LIKE '%да%'


Done.
Out[19]:
pname price category manufacturer
Пряник 19.99 Еда Тульский пряник
Печенье 149.99 Еда WagonWiels

Убрать дубли

  • Дубли не всегда хорошо, и иногда их стоит убирать
    • Помните, что таблицы - мультимножества!

In [20]:
%sql SELECT category from product;


Done.
Out[20]:
category
Еда
Канцелярия
Еда
Техника

In [21]:
%%sql 
-- чтобы убрать дубли используйте слово DISTINCT
SELECT DISTINCT category from product;


Done.
Out[21]:
category
Еда
Канцелярия
Техника

Сортировка результатов

  • Так как Таблица - это мультимножество, то порядок вывода строк не гарантирован. Иногда необходимо выводить строки в определенном порядке

In [13]:
%%sql
-- сортировка результатов
-- сортировка по умолчанию - ascending
SELECT   pname, price, manufacturer
FROM     Product
WHERE    price > 50
ORDER BY  price, pname


Done.
Out[13]:
pname price manufacturer
Печенье 149.99 WagonWiels
Планшет 203.99 Zua

In [22]:
%%sql
-- сортировка результатов
-- тип сортировки каждого компонента определяется индивидуально
SELECT   price, manufacturer
FROM     Product
ORDER BY   manufacturer ASC, price DESC


Done.
Out[22]:
price manufacturer
149.99 WagonWiels
203.99 Zua
19.99 Тульский пряник
29.99 Эксмо

Соединения - JOIN

  • Рассмотрим таблицу компаний.

    company(cname, stockprice, country)


In [32]:
%%sql
drop table if exists product; -- This needs to be dropped if exists, see why further down!
drop table if exists company;
create table company (
    cname varchar primary key, -- company name uniquely identifies the company.
    stockprice money, -- stock price is in money 
    country varchar); -- country is just a string
insert into company values ('Тульский пряник', 25.0, 'Россия');
insert into company values ('Эксмо', 65.0, 'Россия');
insert into company values ('Zua', 15.0, 'Китай');


1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
Out[32]:
[]

In [33]:
%sql select * from company;


Done.
Out[33]:
cname stockprice country
Тульский пряник 25 Россия
Эксмо 65 Россия
Zua 15 Китай

Внешние ключи

  • Допустим мы ходим добавить таблицу продуктов

Product(pname, price, category, manufacturer)

  • Может возникнуть следующая ситуация: есть компания, продающая какие-то продукты, но при этом она отсутствует в таблице компаний.
  • Чтобы избежать это, воспользуемя внешними ключами

Назвнание компании в product ссылается на поле cnma из таблицы company:

foreign key (manufacturer) references company(cname)

  • Замечание: cname должно быть ключом в company!

In [34]:
%%sql drop table if exists product;
pragma foreign_keys = ON; -- WARNING by default off in sqlite
create table product(
       pname varchar primary key, -- name of the product
       price money, -- price of the product
       category varchar, -- category
       manufacturer varchar, -- manufacturer
       foreign key (manufacturer) references company(cname));

insert into product values('Пряник', 19.99, 'Еда', 'Тульский пряник');
insert into product values('Тетрадь', 29.99, 'Канцелярия', 'Эксмо');
insert into product values('Печенье', 149.99, 'Еда', 'Тульский пряник');
insert into product values('Планшет', 203.99, 'Техника', 'Zua');


Done.
Done.
1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
1 rows affected.
Out[34]:
[]

Внешние ключи являются ограничениями таблицы

Что случится при вставке компании, которой не в таблице company?


In [35]:
try:
    %sql insert into product values('MultiTouch', 203.99, 'Household', 'Google');
except Exception as e:
    print (e)
    print ("Rejected!")


(sqlite3.IntegrityError) FOREIGN KEY constraint failed [SQL: "insert into product values('MultiTouch', 203.99, 'Household', 'Google');"]
Rejected!

In [36]:
%%sql
-- the update is rejected!
select * from product;


Done.
Out[36]:
pname price category manufacturer
Пряник 19.99 Еда Тульский пряник
Тетрадь 29.99 Канцелярия Эксмо
Печенье 149.99 Еда Тульский пряник
Планшет 203.99 Техника Zua

Внешние ключи и удаление

  • Что произойдет, если мы удалим компанию ? Несколько вариантов:
    • Запретить удаление (default)
    • Удалить все продукты (добавьте "on delete cascade")
    • Замена на NULL или DEFAULT

Первый вариант (default)- Удаление запрещено


In [37]:
try:
    %sql delete from company where cname = 'Zua';
except Exception as e:
    print (e)
    print ("Disallowed!")


(sqlite3.IntegrityError) FOREIGN KEY constraint failed [SQL: "delete from company where cname = 'Zua';"]
Disallowed!

Второй вариант: удалить все продукты, принадлежащие компании, которую мы удаляем

Необходимо изменить опеределение в create table:

foreign key (manufacturer) references company(cname) on delete cascade

Теперь, когда строка компании удалена, все продукты с указанной компанией, будут также удалены.

Соединения

Product (pname, price, category, manufacturer)
Company (cname, stockPrice, country)

Мы хотим ответить на вопрос

Найти все продукты меньшие 200$, произведенные в России Возвратить их имена и цену.


In [31]:
%%sql
SELECT pname, price
FROM product, company
where manufacturer=cname and country='Россия' and price <= 200;


Done.
Out[31]:
pname price
Пряник 19.99
Тетрадь 29.99
Печенье 149.99

Другой вариант написания запроса.


In [38]:
%%sql -- Часть 1: выбрать все компании из России
SELECT distinct cname -- нужна ли нам уникальность?
from company where country='Россия';


Done.
Out[38]:
cname
Тульский пряник
Эксмо

In [39]:
%%sql -- Part 2: Продукты до 200
select distinct pname, price, manufacturer
from product
where price <= 200;


Done.
Out[39]:
pname price manufacturer
Печенье 149.99 Тульский пряник
Пряник 19.99 Тульский пряник
Тетрадь 29.99 Эксмо

In [40]:
%%sql -- Объединение как cross join
SELECT * 
FROM 
  (SELECT DISTINCT pname, price, manufacturer
   FROM product
   WHERE price <= 200) CheapProducts,
  (SELECT DISTINCT cname
   FROM company
   WHERE country='Россия') JapaneseProducts;


Done.
Out[40]:
pname price manufacturer cname
Печенье 149.99 Тульский пряник Тульский пряник
Печенье 149.99 Тульский пряник Эксмо
Пряник 19.99 Тульский пряник Тульский пряник
Пряник 19.99 Тульский пряник Эксмо
Тетрадь 29.99 Эксмо Тульский пряник
Тетрадь 29.99 Эксмо Эксмо

In [41]:
%%sql
-- Фильтруем cross join
SELECT DISTINCT pname, price
FROM 
  (SELECT DISTINCT pname, price, manufacturer
   FROM product
   WHERE price <= 200) CheapProducts,
  (SELECT distinct cname
   FROM company
   WHERE country='Россия') JapaneseProducts
WHERE cname = manufacturer;


Done.
Out[41]:
pname price
Печенье 149.99
Пряник 19.99
Тетрадь 29.99

Примечания

  • Есть множество логических вариантов написать один и тот же запрос
    • Этот факт будет использоваться для оптимизации

Дубли после соединения

Замечание - могут возникнуть дубли после соединения...


In [42]:
%%sql -- duplicate answer
SELECT Country
FROM Product, Company
WHERE  Manufacturer=CName AND Category='Еда';


Done.
Out[42]:
country
Россия
Россия

In [ ]:


In [ ]: